#include "SkyBoxSample.h"
#include "Application.h"
#include "utils.h"
#include "Utility.h"
namespace RayTracer
{
	void SkyBoxSample::Init()
	{
		curIndex = 0.0;
		isGenerate = false;
		mImageParam = ImageParam(800, 500, "SkyBox.png");
		lookfrom = vec3(0.0, 0.2, 1.5);
		lookat = vec3(0.0, 0.0, 0.0);
		fov = 60;
		upColor = vec3(0.5, 0.7, 1.0);
		downColor = vec3(1.0);
		groundColor = vec3(0.8);
		sphereColor = vec3(1.0);
		floorColor = vec3(1.0);
		leftColor = vec3(1.0);
		rightColor = vec3(1.0);
		frontColor = vec3(1.0);
		roughness = 0.3;
		ir = 1.5;
		radius = 0.2;
		currMat = 0;
	}

	void SkyBoxSample::Render()
	{
		floorMat = std::make_shared<Lambertian>(groundColor);
		metalMat = std::make_shared<Metal>(groundColor,roughness);
		glassMat = std::make_shared<Glass>(ir);
		xy_RectMat = std::make_shared<Lambertian>(frontColor);
		xz_RectMat = std::make_shared<Lambertian>(floorColor);
		yz_RectMat = std::make_shared<Lambertian>(leftColor);
		yz_RectRMat = std::make_shared<Lambertian>(rightColor);
		
		switch (currMat)
		{
		case 0 :
			floor = std::make_shared<Sphere>(vec3(0, 0, 0), radius, floorMat);
			break;
		case 1:
			floor = std::make_shared<Sphere>(vec3(0, 0, 0), radius, metalMat);
			break;
		case 2:
			floor = std::make_shared<Sphere>(vec3(0, 0, 0), radius, glassMat);
			break;
		}
		xy_Rect = std::make_shared<XY_Rect>(-0.5, 0.5, -0.5, 0.5, -0.5, xy_RectMat);
		xz_Rect = std::make_shared<XZ_Rect>(-0.5, 0.5, -0.5, 0.5, -0.5, xz_RectMat);
		yz_Rect = std::make_shared<YZ_Rect>(-0.5, 0.5, -0.5, 0.5, -0.5, yz_RectMat);
		yz_RectR = std::make_shared<YZ_Rect>(-0.5, 0.5, -0.5, 0.5, 0.5, yz_RectRMat);

		world.add(floor);
		world.add(xy_Rect);
		world.add(xz_Rect);
		world.add(yz_Rect);
		world.add(yz_RectR);

		int width = mImageParam.mWidth;
		int height = mImageParam.mHeight;
		camera = Camera(lookfrom, lookat, fov);

		uint8_t* pixels = new uint8_t[width * height * 3];
		int index = 0;
		for (int j = height - 1; j >= 0; j--) {
			for (int i = 0; i < width; ++i) {
				vec3 color = vec3(0.0f);
				for (int s = 0; s < samples_per_pixel; s++)
				{
					auto u = (i + Random01()) / (width - 1);
					auto v = (j + Random01()) / (height - 1);
					Ray r = camera.GetRay(u, v);
					color += RayColor(r, world, maxDepth);
				}
				auto scale = 1.0 / samples_per_pixel;
				color *= scale;

				GammaCorrect(color);
				pixels[index++] = static_cast<uint8_t>(256 * clamp(color.r, 0.0, 0.999));
				pixels[index++] = static_cast<uint8_t>(256 * clamp(color.g, 0.0, 0.999));
				pixels[index++] = static_cast<uint8_t>(256 * clamp(color.b, 0.0, 0.999));
				curIndex++;
			}
		}
		Utils::SaveImage(("Images/" + mImageParam.mName).c_str(), width, height, 3, pixels);
		isGenerate = true;
	}

	void SkyBoxSample::ImGui()
	{
		ImGui::Text(u8"SkyBox");
		ImGui::Checkbox(u8"Normal", &isNormal);
		if (isNormal)
		{
			samples_per_pixel = 1;
		}
		else
		{
			ImGui::SliderInt(u8"Spp", &samples_per_pixel, 1, 500);
		}
		ImGui::SliderInt(u8"MaxDepth", &maxDepth, 1, 50);

		ImGui::Separator();
		if (ImGui::CollapsingHeader(u8"Sky Color"))
		{
			ImGui::ColorEdit3(u8"High", &upColor[0]);
			ImGui::ColorEdit3(u8"Low", &downColor[0]);
		}
		ImGui::Separator();
		if (ImGui::CollapsingHeader(u8"Camera"))
		{
			ImGui::InputFloat3(u8"Camera Position", &lookfrom[0]);
			ImGui::SliderFloat("Fov", &fov, 20.0, 180.0);
		}
		ImGui::Separator();
		if (ImGui::CollapsingHeader(u8"Wall Color"))
		{
			ImGui::ColorEdit3(u8"Front", &frontColor[0]);
			ImGui::ColorEdit3(u8"Floor", &floorColor[0]);
			ImGui::ColorEdit3(u8"Left", &leftColor[0]);
			ImGui::ColorEdit3(u8"Right", &rightColor[0]);
		}
		ImGui::Separator();
		if (ImGui::CollapsingHeader(u8"Sphere")) 
		{
			const char* items[] = { "Labertain", "Metal", "Glass"};
			static const char* current_item = "Labertain";

			if (ImGui::BeginCombo("Material", current_item)) 
			{
				for (int n = 0; n < IM_ARRAYSIZE(items); n++)
				{
					bool is_selected = (current_item == items[n]); 
					if (ImGui::Selectable(items[n], is_selected)) 
					{
						current_item = items[n];
						currMat = n;
					}
					
					if (is_selected)
						ImGui::SetItemDefaultFocus();
						
				}
				ImGui::EndCombo();
			}
			switch (currMat)
			{
			case 0:
				ImGui::ColorEdit3(u8"Sphere Color", &groundColor[0]);
				ImGui::SliderFloat(u8"Radius", &radius, 0.0, 1.0);
				break;
			case 1:
				ImGui::ColorEdit3(u8"Sphere Color", &groundColor[0]);
				ImGui::SliderFloat(u8"Radius", &radius, 0.0, 1.0);
				ImGui::SliderFloat("Roughness",&roughness,0.0,1.0);
				break;
			case 2:
				ImGui::SliderFloat(u8"Radius", &radius, 0.0, 1.0);
				ImGui::SliderFloat("IR", &ir, 0.0, 3.0);
				break;
			}
		}
		ImGui::Separator();
		auto w = (float)mImageParam.mWidth;
		auto h = (float)mImageParam.mHeight;
		auto progress = ((float)curIndex / (w*h));
		ImGui::ProgressBar(progress);
		ImGui::Separator();
		if (ImGui::Button(u8"Generate")) {
			std::thread t = std::thread([=]() {
				Render();
				});
			t.detach();
		}
		if (isGenerate)
		{
			mTexHandle = Utils::LoadTexture(("Images/" + mImageParam.mName).c_str());
			Application::GetInstance()->SetTexHandle(mTexHandle);
			isGenerate = false;
			curIndex = 0;
		}
		ImGui::SameLine();
		if (ImGui::Button("Reset")) { Reset(); }
	}

	void SkyBoxSample::ShutDown()
	{
		mTexHandle = 0;
		Application::GetInstance()->SetTexHandle(mTexHandle);
	}

	vec3 SkyBoxSample::RayColor(const Ray & r, const Hitable& world, int depth)
	{
		HitRecord rec;
		if (depth <= 0) return vec3(0.0f);
		if (world.Hit(r, 0.001, infinity, rec))
		{
			vec3 res;
			if (isNormal)
			{
				res = 0.5f * (rec.normal + vec3(1.0f));
			}
			else
			{
				Ray scattered;
				vec3 attenuation;
				if (rec.matPtr->Scatter(r, rec, attenuation, scattered))
					return attenuation * RayColor(scattered, world, depth - 1);
				return vec3(1.0f);
			}
			return res;
		}

		vec3 unit_direction = glm::normalize(r.direction());
		auto t = 0.5f*(unit_direction.y + 1.0f);
		return (1.0f - t)*downColor + t * upColor;
	}
	void SkyBoxSample::Reset()
	{
		mTexHandle = 0;
		Application::GetInstance()->SetTexHandle(mTexHandle);
		curIndex = 0.0;
		isGenerate = false;
		lookfrom = vec3(0.0, 0.2, 1.5);
		lookat = vec3(0.0, 0.0, 0.0);
		fov = 60;
		upColor = vec3(0.5, 0.7, 1.0);
		downColor = vec3(1.0);
		groundColor = vec3(0.8);
		sphereColor = vec3(1.0);
		floorColor = vec3(1.0);
		leftColor = vec3(1.0);
		rightColor = vec3(1.0);
		frontColor = vec3(1.0);
		roughness = 0.3;
		ir = 1.5;
		radius = 0.2;
		currMat = 0;
	}
}